package ontology;

import java.util.EnumSet;
import ontology.Comparison;
import ontology.Control;
import ontology.Direction;
import ontology.Polarity;

public enum InteractionVerb {

    ACETYLATED_BY("acetylated by", "PZB", "ACETYLATION"),
    ACETYLATES("acetylates", "PZF", "ACETYLATION"),
    ACTIVATED_BY("activated by", "PZB", "PROTEIN_REGULATION"),
    ACTIVATES("activates", "PZF", "PROTEIN_REGULATION"),
    ASSOCIATES_WITH("associates with", "PZZ", "BINDING"),
    BINDS("binds", "PZZ", "BINDING"),
    CLEAVAGE_INDUCED_BY("cleavage induced by", "PPB", "CLEAVAGE"),
    CLEAVED_BY("cleaved by", "PZB", "CLEAVAGE"),
    CLEAVES("cleaves", "PZF", "CLEAVAGE"),
    COLOCALIZES_WITH("co-localizes with", "PZZ", "COLOCALISATION"),
    COMPETES_WITH("competes with", "NZZ", "PROTEIN_REGULATION"),
    COMPLEXES_WITH("complexes with", "PZZ", "BINDING"),
    COOPERATES_WITH("cooperates with", "PZZ", "PROTEIN_REGULATION"),
    DECREASES_PHOSPHORYLATION_OF("decreases phosphorylation of", "PZF", "PHOSPHORYLATION"),
    DEGLYCOSYLATES("deglycosylates", "NZF", "GLYCOSYLATION"),
    DEGRADED_BY("degraded by", "PZB", "DEGREDATION"),
    DEGRADES("degrades", "PZF", "DEGREDATION"),
    DEPOLYMERIZES("depolymerizes", "NZF", "POLYMERIZATION"),
    DISRUPTS("disrupts", "NZF", "PROTEIN_REGULATION"),
    DOWNREGULATED_BY("downregulated by", "NZB", "GENE_REGULATION"),
    DOWNREGULATES("downregulates", "NZF", "GENE_REGULATION"),
    ENHANCED_BY("enhanced by", "PZB", "PROTEIN_REGULATION"),
    ENHANCES("enhances", "PZF", "PROTEIN_REGULATION"),
    ENHANCES_POLYMERIZATION_OF("enhances polymerization of", "PPF", "POLYMERIZATION"),
    EXPORTED_BY("exported by", "NZB", "IMPORTATION"),
    FRACTIONATES_WITH("fractionates with", "PZZ", "LOCATION_EVENT"),
    GLYCOSYLATED_BY("glycosylated by", "PZB", "GLYCOSYLATION"),
    IMPORTED_BY("imported by", "PZB", "IMPORTATION"),
    INACTIVATES("inactivates", "NZF", "PROTEIN_REGULATION"),
    INCORPORATES("incorporates", "PZF", "INCORPORATION"),
    INDUCES_ACCUMULATION_OF("induces accumulation of", "PPF", "GENE_REGULATION"),
    INDUCES_ACETYLATION_OF("induces acetylation of", "PPF", "ACETYLATION"),
    INDUCES_CLEAVAGE_OF("induces cleavage of", "PPF", "CLEAVAGE"),
    INDUCES_COMPLEX_WITH("induces complex with", "PPZ", "BINDING"),
    INDUCES_PHOSPHORYLATION_OF("induces phosphorylation of", "PPF", "PHOSPHORYLATION"),
    INDUCES_REARRANGEMENT_OF("induces rearrangement of", "PPF", "REARRANGEMENT"),
    INDUCES_RELEASE_OF("induces release of", "PPF", "RELEASE"),
    INHIBITED_BY("inhibited by", "NZB", "PROTEIN_REGULATION"),
    INHIBITS("inhibits", "NZF", "PROTEIN_REGULATION"),
    INHIBITS_ACETYLATION_OF("inhibits acetylation of", "PNF", "ACETYLATION"),
    INTERACTS_WITH("interacts with", "ZZZ", "UNINFORMATIVE_INTERACTION"),
    ISOMERIZED_BY("isomerized by", "PZB", "ISOMERIZATION"),
    MEDIATED_BY("mediated by", "ZZB", "REGULATION"),
    METHYLATED_BY("methylated by", "PZB", "METHYLATION"),
    MODIFIED_BY("modified by", "PZB", "MODIFICATION"),
    MODULATED_BY("modulated by", "ZZB", "REGULATION"),
    MODULATES("modulates", "ZZF", "REGULATION"),
    MYRISTOYLATED_BY("myristoylated by", "PZB", "MYRISTOYLATION"),
    PALMITOYLATED_BY("palmitoylated by", "PZB", "PALMITOYLATION"),
    PHOSPHORYLATED_BY("phosphorylated by", "PZB", "PHOSPHORYLATION"),
    PHOSPHORYLATES("phosphorylates", "PZF", "PHOSPHORYLATION"),
    POLARIZES("polarizes", "PZF", "POLARIZATION"),
    PROCESSED_BY("processed by", "PZB", "MODIFICATION"),
    RECRUITED_BY("recruited by", "PZB", "LOCATION_EVENT"),
    RECRUITS("recruits", "PZF", "LOCATION_EVENT"),
    REGULATED_BY("regulated by", "ZZB", "REGULATION"),
    REGULATES("regulates", "ZZF", "REGULATION"),
    RELOCALIZED_BY("relocalized by", "PZB", "RELOCALIZATION"),
    RELOCALIZES("relocalizes", "PNF", "RELOCALIZATION"),
    REQUIRES("requires", "PZB", "REGULATION"),
    SENSITIZES("sensitizes", "PZF", "PROTEIN_REGULATION"),
    STABILIZES("stabilizes", "PPF", "BINDING"),
    STIMULATED_BY("stimulated by", "PZB", "REGULATION"),
    STIMULATES("stimulates", "PZF", "REGULATION"),
    SYNERGIZES_WITH("synergizes with", "PZZ", "PROTEIN_REGULATION"),
    UBIQUITINATED_BY("ubiquitinated by", "PZB", "UBIQUITINATION"),
    UPREGULATED_BY("upregulated by", "PZB", "GENE_REGULATION"),
    UPREGULATES("upregulates", "PZF", "GENE_REGULATION");
    private final String description;
    private final String attString;
    private final Polarity polarity;
    private final Control control;
    private final Direction direction;
    private final String parentString;
    public static int NOT_RELATED = 0;
    public static int A_IS_ANCESTOR_OF_B = 1;
    public static int B_IS_ANCESTOR_OF_A = 2;
    public static int ARE_SIBLINGS = 3;

    private InteractionVerb(String description, String attString, String parentString) {
        this.description = description;
        this.attString = attString;
        this.parentString = parentString;
        polarity = getPolarity(attString);
        control = getControl(attString);
        direction = getDirection(attString);
//        code = createCode();

    }

    private Control getControl(String attString) {
        char c = attString.charAt(1);
        if (c == 'P') {
            return Control.POSITIVE;
        }
        if (c == 'N') {
            return Control.NEGATIVE;
        }
        if (c == 'Z') {
            return Control.NULL;
        }
        return null;
    }

    private Direction getDirection(String attString) {
        char d = attString.charAt(2);
        if (d == 'F') {
            return Direction.FORWARD;
        }
        if (d == 'B') {
            return Direction.BACKWARD;
        }
        if (d == 'Z') {
            return Direction.NULL;
        }
        return null;
    }

    private Polarity getPolarity(String attString) {
        char p = attString.charAt(0);
        if (p == 'P') {
            return Polarity.POSITIVE;
        }
        if (p == 'N') {
            return Polarity.NEGATIVE;
        }
        if (p == 'Z') {
            return Polarity.NULL;
        }
        return null;
    }

    public static InteractionVerb getInteractionVerbByDescription(String description) {
        for (InteractionVerb it : EnumSet.allOf(InteractionVerb.class)) {
            if (description.equalsIgnoreCase(it.getDescription())) {
                return it;
            }
        }
        System.out.println("did not find a InteractionType to match the description " + description);
        return null;
    }

    public static InteractionVerb getInteractionVerbByName(String name) {
        for (InteractionVerb it : EnumSet.allOf(InteractionVerb.class)) {
            if (name.equalsIgnoreCase(it.name())) {
                return it;
            }
        }
        System.out.println("did not find a DbInteraction to match the name " + name);
        return null;
    }

    public String getDescription() {
        return description;
    }

    public String getAttString() {
        return attString;
    }

    public Control getControl() {
        return control;
    }

    public Direction getDirection() {
        return direction;
    }

    public Polarity getPolarity() {
        return polarity;
    }

    public HasParents getParent() {
        for (ParentInteraction pi : ParentInteraction.values()) {
            if (pi.name().equals(this.parentString)) {
                return pi;
            }
        }
        System.out.println("no parent interaction was found matching: " + this.parentString);
        return null;

    }

    public static int couldBeSameAction(InteractionVerb a, InteractionVerb b) {
        int informative = informative(a, b);
        if (informative != 3) {
            if (informative == 0) {
                return ARE_SIBLINGS;
            } else if (informative == 1) {
                return A_IS_ANCESTOR_OF_B;
            } else {
                return B_IS_ANCESTOR_OF_A;
            }
        }


        if (opposing(a, b)) {
            return NOT_RELATED;
        }

        if (a.getParent().equals(b.getParent())) {
            return ARE_SIBLINGS;
        }

        if (a.isAncestor(b)) {
            return A_IS_ANCESTOR_OF_B;
        }
        if (b.isAncestor(a)) {
            return B_IS_ANCESTOR_OF_A;
        }

        return NOT_RELATED;
    }

    private boolean isAncestor(InteractionVerb other) {
        if (this.getParent().equals(other.getParent())) {
            return false;
        }
        return aIsAncestorOfB(this.getParent(), other.getParent());
    }

    private boolean aIsAncestorOfB(HasParents a, HasParents b) {
        if (a.equals(b)) {
            System.out.println("error: you are asking if a is ancestor of b when a==b");
            return false;
        }
        if (a.equals(ParentInteraction.UNINFORMATIVE_INTERACTION)) {
            return true;
        } else if (b.equals(ParentInteraction.UNINFORMATIVE_INTERACTION)) {
            return false;
        } else if (a.equals(b.getParent())) {
            return true;
        } else {
            return aIsAncestorOfB(a, b.getParent());
        }

    }

    public static int informative(InteractionVerb a, InteractionVerb b) {
        if (a.equals(InteractionVerb.INTERACTS_WITH) &&
                b.equals(InteractionVerb.INTERACTS_WITH)) {
            return 0;
        }
        if (a.equals(InteractionVerb.INTERACTS_WITH)) {
            return 1;
        }
        if (b.equals(InteractionVerb.INTERACTS_WITH)) {
            return 2;
        }
        return 3;
    }

    private static boolean opposing(InteractionVerb a, InteractionVerb b) {

        Direction aDir = a.getDirection();
        Direction bDir = b.getDirection();
        Polarity aPol = a.getPolarity();
        Polarity bPol = b.getPolarity();
        Control aCon = a.getControl();
        Control bCon = b.getControl();
//        System.out.println(aDir.name()+", "+bDir.name());
        if (aDir.compareWith(bDir).equals(Comparison.HAS_OPPOSING)) {
            return true;
        }
        if (aPol.compareWith(bPol).equals(Comparison.HAS_OPPOSING)) {
            return true;
        }
        if (aCon.compareWith(bCon).equals(Comparison.HAS_OPPOSING)) {
            return true;
        }
        return false;

    }

    public String getParentString() {
        return parentString;
    }

    public boolean isPhysical() {
        return isPhysical(this.getParent());
    }

    private boolean isPhysical(HasParents pi) {
        if (pi.equals(ParentInteraction.UNINFORMATIVE_INTERACTION)) {
            return false;
        }
        boolean parentPhysical = pi.equals(ParentInteraction.PHYSICAL);
        if (!parentPhysical) {
            return isPhysical(pi.getParent());
        } else {
            return parentPhysical;
        }

    }
}
